diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aaf3452 --- /dev/null +++ b/.gitignore @@ -0,0 +1,131 @@ + +# Created by https://www.gitignore.io/api/macos,windows,linux,node + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/macos,windows,linux,node + +yarn.lock +package-lock.json \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..88d901f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +language: node_js +node_js: + - 9 + - 8 + - 6 + - 4 + +# Trigger a push build on master and greenkeeper branches + PRs build on every branches +# Avoid double build on PRs (See https://github.com/travis-ci/travis-ci/issues/1147) +branches: + only: + - master + - /^greenkeeper.*$/ + +# Retry install on fail to avoid failing a build on network/disk/external errors +install: + - travis_retry npm install + +script: + - npm run test + +jobs: + include: + - stage: release + node_js: node + os: linux + script: npm run test + after_success: + - npm run codecov + - npm run semantic-release diff --git a/.yarnrc b/.yarnrc new file mode 100644 index 0000000..acaaffd --- /dev/null +++ b/.yarnrc @@ -0,0 +1 @@ +--install.no-lockfile true diff --git a/README.md b/README.md index 1ac0852..35a9b93 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,54 @@ # env-ci -Get environment variables exposed by CI services + +Get environment variables exposed by CI services. + +[![Travis](https://img.shields.io/travis/pvdlg/env-ci.svg)](https://travis-ci.org/pvdlg/env-ci) +[![Codecov](https://img.shields.io/codecov/c/github/pvdlg/env-ci.svg)](https://codecov.io/gh/pvdlg/env-ci) +[![Greenkeeper badge](https://badges.greenkeeper.io/pvdlg/env-ci.svg)](https://greenkeeper.io/) + +Adapted from [codecov-node](https://github.com/codecov/codecov-node/blob/master/lib/detect.js). + +## Install + +```bash +$ npm install --save env-ci +``` + +## Usage + +```js +const {isCi, service, commit, build, branch, job, pr, isPr, slug, root} = require('env-ci'); +``` + +## Variables + +| Variable | Description | +|-----------|-----------------------------------------------------------------------------------| +| `isCi` | `true` is running on a CI, `false` otherwise | +| `service` | CI service name | +| `commit` | Commit sha that triggered the CI build | +| `build` | CI service build number | +| `branch` | Git branch being built or targeted by a pull request | +| `job` | CI service job number | +| `pr` | Pull Request number | +| `isPr` | `true` is the build has been triggered by a Pull Request, `false` otherwise | +| `slug` | The slug (in form: owner_name/repo_name) of the repository currently being built. | +| `root` | The path to the directory where the repository is being built | + +## Supported CI + +| Service | `isCi` | `service` | `commit` | `build` | `branch` | `job` | `pr` | `isPr` | `slug` | `root` | +|-------------------------------------------------------------|:------:|:---------:|:--------:|:-------:|:--------:|:-----:|:----:|:------:|:------:|:------:| +| [AppVeyor](https://www.appveyor.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Buildkite](https://buildkite.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | +| [Circleci](https://circleci.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| [Codeship](https://codeship.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | +| [Drone](http://try.drone.io) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| [Gitlab CI](https://about.gitlab.com/features/gitlab-ci-cd) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +| [Jenkins](https://jenkins-ci.org) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | +| [Semaphore](https://semaphoreci.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Shippable](https://www.shippable.com) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Travis](https://travis-ci.org/) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [Wercker](http://www.wercker.com/) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | + +If none of the above CI services is detected, `commit` and `branch` are determined based on the local Git repository, and `isCi` is determined based on the `CI` environment variable. diff --git a/index.js b/index.js new file mode 100644 index 0000000..d9e9281 --- /dev/null +++ b/index.js @@ -0,0 +1,26 @@ +'use strict'; + +const git = require('./lib/git'); + +const services = { + travis: require('./lib/travis'), + circle: require('./lib/circle'), + appveyor: require('./lib/appveyor'), + wercker: require('./lib/wercker'), + codeship: require('./lib/codeship'), + jenkins: require('./lib/jenkins'), + semaphore: require('./lib/semaphore'), + shippable: require('./lib/shippable'), + drone: require('./lib/drone'), + buildkite: require('./lib/buildkite'), + gitlab: require('./lib/gitlab'), +}; + +module.exports = () => { + for (const name of Object.keys(services)) { + if (services[name].detect()) { + return Object.assign({isCi: true}, services[name].configuration()); + } + } + return Object.assign({isCi: Boolean(process.env.CI)}, git.configuration()); +}; diff --git a/lib/appveyor.js b/lib/appveyor.js new file mode 100644 index 0000000..85d0a93 --- /dev/null +++ b/lib/appveyor.js @@ -0,0 +1,20 @@ +// https://www.appveyor.com/docs/environment-variables/ + +module.exports = { + detect() { + return Boolean(process.env.APPVEYOR); + }, + configuration() { + return { + service: 'appveyor', + commit: process.env.APPVEYOR_REPO_COMMIT, + build: process.env.APPVEYOR_BUILD_NUMBER, + branch: process.env.APPVEYOR_REPO_BRANCH, + job: process.env.APPVEYOR_JOB_NUMBER, + pr: process.env.APPVEYOR_PULL_REQUEST_NUMBER, + isPr: Boolean(process.env.APPVEYOR_PULL_REQUEST_NUMBER), + slug: process.env.APPVEYOR_REPO_NAME, + root: process.env.APPVEYOR_BUILD_FOLDER, + }; + }, +}; diff --git a/lib/buildkite.js b/lib/buildkite.js new file mode 100644 index 0000000..116541c --- /dev/null +++ b/lib/buildkite.js @@ -0,0 +1,19 @@ +// https://buildkite.com/docs/builds/environment-variables + +module.exports = { + detect() { + return Boolean(process.env.BUILDKITE); + }, + configuration() { + return { + service: 'buildkite', + build: process.env.BUILDKITE_BUILD_NUMBER, + commit: process.env.BUILDKITE_COMMIT, + branch: process.env.BUILDKITE_BRANCH, + slug: `${process.env.BUILDKITE_ORGANIZATION_SLUG}/${process.env.BUILDKITE_PROJECT_SLUG}`, + pr: process.env.BUILDKITE_PULL_REQUEST === 'false' ? undefined : process.env.BUILDKITE_PULL_REQUEST, + isPr: process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false', + root: process.env.BUILDKITE_BUILD_CHECKOUT_PATH, + }; + }, +}; diff --git a/lib/circle.js b/lib/circle.js new file mode 100644 index 0000000..88e435f --- /dev/null +++ b/lib/circle.js @@ -0,0 +1,19 @@ +// https://circleci.com/docs/1.0/environment-variables/ + +module.exports = { + detect() { + return Boolean(process.env.CIRCLECI); + }, + configuration() { + return { + service: 'circleci', + build: `${process.env.CIRCLE_BUILD_NUM}.${process.env.CIRCLE_NODE_INDEX}`, + job: `${process.env.CIRCLE_BUILD_NUM}.${process.env.CIRCLE_NODE_INDEX}`, + commit: process.env.CIRCLE_SHA1, + branch: process.env.CIRCLE_BRANCH, + pr: process.env.CI_PULL_REQUEST ? process.env.CI_PULL_REQUEST.split('/').pop() : undefined, + isPr: Boolean(process.env.CI_PULL_REQUEST), + slug: `${process.env.CIRCLE_PROJECT_USERNAME}/${process.env.CIRCLE_PROJECT_REPONAME}`, + }; + }, +}; diff --git a/lib/codeship.js b/lib/codeship.js new file mode 100644 index 0000000..8867c6c --- /dev/null +++ b/lib/codeship.js @@ -0,0 +1,16 @@ +// https://documentation.codeship.com/basic/builds-and-configuration/set-environment-variables/ + +module.exports = { + detect() { + return process.env.CI_NAME && process.env.CI_NAME === 'codeship'; + }, + configuration() { + return { + service: 'codeship', + build: process.env.CI_BUILD_NUMBER, + commit: process.env.CI_COMMIT_ID, + branch: process.env.CI_BRANCH, + slug: process.env.CI_REPO_NAME, + }; + }, +}; diff --git a/lib/drone.js b/lib/drone.js new file mode 100644 index 0000000..f8e1098 --- /dev/null +++ b/lib/drone.js @@ -0,0 +1,19 @@ +// http://readme.drone.io/0.5/usage/environment-reference/ + +module.exports = { + detect() { + return Boolean(process.env.DRONE); + }, + configuration() { + return { + service: 'drone', + commit: process.env.DRONE_COMMIT_SHA, + build: process.env.DRONE_BUILD_NUMBER, + branch: process.env.DRONE_BRANCH, + job: process.env.DRONE_JOB_NUMBER, + pr: process.env.DRONE_PULL_REQUEST, + isPr: process.env.DRONE_BUILD_EVENT === 'pull_request', + slug: `${process.env.DRONE_REPO_OWNER}/${process.env.DRONE_REPO_NAME}`, + }; + }, +}; diff --git a/lib/git.js b/lib/git.js new file mode 100644 index 0000000..40ca59e --- /dev/null +++ b/lib/git.js @@ -0,0 +1,26 @@ +const execa = require('execa'); + +function configuration() { + return { + commit: head(), + branch: branch(), + }; +} + +function head() { + try { + return execa.sync('git', ['rev-parse', 'HEAD']).stdout; + } catch (err) { + return undefined; + } +} + +function branch() { + try { + return execa.sync('git', ['rev-parse', '--abbrev-ref', 'HEAD']).stdout; + } catch (err) { + return undefined; + } +} + +module.exports = {configuration, head, branch}; diff --git a/lib/gitlab.js b/lib/gitlab.js new file mode 100644 index 0000000..0bb0b45 --- /dev/null +++ b/lib/gitlab.js @@ -0,0 +1,23 @@ +// https://docs.gitlab.com/ce/ci/variables/README.html + +module.exports = { + detect() { + return Boolean(process.env.GITLAB_CI); + }, + configuration() { + return { + service: 'gitlab', + commit: process.env.CI_COMMIT_SHA, + build: process.env.CI_JOB_NAME, + job: process.env.CI_JOB_STAGE, + branch: process.env.CI_COMMIT_REF_NAME, + slug: process.env.CI_REPOSITORY_URL + ? process.env.CI_REPOSITORY_URL.split('/') + .slice(3, 5) + .join('/') + .replace('.git', '') + : undefined, + root: process.env.CI_PROJECT_DIR, + }; + }, +}; diff --git a/lib/jenkins.js b/lib/jenkins.js new file mode 100644 index 0000000..84dda50 --- /dev/null +++ b/lib/jenkins.js @@ -0,0 +1,20 @@ +const git = require('../lib/git'); + +// https://wiki.jenkins.io/display/JENKINS/Building+a+software+project + +module.exports = { + detect() { + return Boolean(process.env.JENKINS_URL); + }, + configuration() { + return { + service: 'jenkins', + commit: process.env.ghprbActualCommit || process.env.GIT_COMMIT || git.head(), + branch: process.env.ghprbSourceBranch || process.env.GIT_BRANCH || process.env.BRANCH_NAME, + build: process.env.BUILD_NUMBER, + root: process.env.WORKSPACE, + pr: process.env.ghprbPullId || process.env.CHANGE_ID, + isPr: Boolean(process.env.ghprbPullId || process.env.CHANGE_ID), + }; + }, +}; diff --git a/lib/semaphore.js b/lib/semaphore.js new file mode 100644 index 0000000..4fbd29d --- /dev/null +++ b/lib/semaphore.js @@ -0,0 +1,21 @@ +const git = require('../lib/git'); + +// https://semaphoreci.com/docs/available-environment-variables.html + +module.exports = { + detect() { + return Boolean(process.env.SEMAPHORE); + }, + configuration() { + return { + service: 'semaphore', + commit: git.head(), + build: process.env.SEMAPHORE_BUILD_NUMBER, + branch: process.env.BRANCH_NAME, + pr: process.env.PULL_REQUEST_NUMBER, + isPr: Boolean(process.env.PULL_REQUEST_NUMBER), + slug: process.env.SEMAPHORE_REPO_SLUG, + root: process.env.SEMAPHORE_PROJECT_DIR, + }; + }, +}; diff --git a/lib/shippable.js b/lib/shippable.js new file mode 100644 index 0000000..300c6da --- /dev/null +++ b/lib/shippable.js @@ -0,0 +1,20 @@ +// http://docs.shippable.com/ci/env-vars/#stdEnv + +module.exports = { + detect() { + return Boolean(process.env.SHIPPABLE); + }, + configuration() { + return { + service: 'shippable', + commit: process.env.COMMIT, + build: process.env.BUILD_NUMBER, + branch: process.env.BASE_BRANCH || process.env.BRANCH, + job: process.env.JOB_NUMBER, + pr: process.env.PULL_REQUEST === 'false' ? undefined : process.env.PULL_REQUEST, + isPr: process.env.IS_PULL_REQUEST && process.env.IS_PULL_REQUEST === 'true', + slug: process.env.SHIPPABLE_REPO_SLUG, + root: process.env.SHIPPABLE_BUILD_DIR, + }; + }, +}; diff --git a/lib/travis.js b/lib/travis.js new file mode 100644 index 0000000..9f66fa2 --- /dev/null +++ b/lib/travis.js @@ -0,0 +1,20 @@ +// https://docs.travis-ci.com/user/environment-variables/ + +module.exports = { + detect() { + return Boolean(process.env.TRAVIS); + }, + configuration() { + return { + service: 'travis', + commit: process.env.TRAVIS_COMMIT, + build: process.env.TRAVIS_BUILD_NUMBER, + branch: process.env.TRAVIS_BRANCH, + job: process.env.TRAVIS_JOB_NUMBER, + pr: process.env.TRAVIS_PULL_REQUEST === 'false' ? undefined : process.env.TRAVIS_PULL_REQUEST, + isPr: process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false', + slug: process.env.TRAVIS_REPO_SLUG, + root: process.env.TRAVIS_BUILD_DIR, + }; + }, +}; diff --git a/lib/wercker.js b/lib/wercker.js new file mode 100644 index 0000000..17bc983 --- /dev/null +++ b/lib/wercker.js @@ -0,0 +1,17 @@ +// http://devcenter.wercker.com/docs/environment-variables/available-env-vars#hs_cos_wrapper_name + +module.exports = { + detect() { + return Boolean(process.env.WERCKER_MAIN_PIPELINE_STARTED); + }, + configuration() { + return { + service: 'wercker', + commit: process.env.WERCKER_GIT_COMMIT, + build: process.env.WERCKER_MAIN_PIPELINE_STARTED, + branch: process.env.WERCKER_GIT_BRANCH, + slug: `${process.env.WERCKER_GIT_OWNER}/${process.env.WERCKER_GIT_REPOSITORY}`, + root: process.env.WERCKER_ROOT, + }; + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c71e20b --- /dev/null +++ b/package.json @@ -0,0 +1,91 @@ +{ + "name": "env-ci", + "description": "Get environment variables exposed by CI services", + "version": "0.0.0-development", + "author": "Pierre Vanduynslager (https://github.com/pvdlg)", + "bugs": { + "url": "https://github.com/pvdlg/github-parser/issues" + }, + "dependencies": { + "execa": "^0.8.0" + }, + "devDependencies": { + "ava": "^0.23.0", + "codecov": "^3.0.0", + "eslint-config-prettier": "^2.3.0", + "eslint-plugin-prettier": "^2.3.0", + "nyc": "^11.1.0", + "prettier": "~1.8.2", + "semantic-release": "^11.0.0", + "tempy": "^0.2.1", + "xo": "^0.18.2" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "lib", + "index.js" + ], + "homepage": "https://github.com/pvdlg/env-ci#readme", + "keywords": [ + "appveyor", + "buildkite", + "ci", + "circle", + "codeship", + "drone", + "environment", + "gitlab", + "jenkins", + "semaphore", + "shippable", + "travis", + "variable", + "wercker" + ], + "license": "MIT", + "main": "index.js", + "nyc": { + "include": [ + "lib/**/*.js", + "index.js" + ], + "reporter": [ + "json", + "text", + "html" + ], + "all": true + }, + "prettier": { + "printWidth": 120, + "singleQuote": true, + "bracketSpacing": false, + "trailingComma": "es5" + }, + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/pvdlg/env-ci.git" + }, + "scripts": { + "codecov": "codecov -f coverage/coverage-final.json", + "lint": "xo", + "semantic-release": "semantic-release", + "test": "xo && nyc ava -v" + }, + "xo": { + "extends": [ + "prettier" + ], + "plugins": [ + "prettier" + ], + "rules": { + "prettier/prettier": 2 + } + } +} diff --git a/test/appveyor.test.js b/test/appveyor.test.js new file mode 100644 index 0000000..2672e3a --- /dev/null +++ b/test/appveyor.test.js @@ -0,0 +1,48 @@ +import test from 'ava'; +import appveyor from '../lib/appveyor'; + +test('Push', t => { + process.env.APPVEYOR = 'true'; + process.env.APPVEYOR_JOB_NUMBER = '1234'; + process.env.APPVEYOR_REPO_COMMIT = '5678'; + process.env.APPVEYOR_BUILD_NUMBER = '91011'; + process.env.APPVEYOR_REPO_BRANCH = 'master'; + process.env.APPVEYOR_REPO_NAME = 'owner/repo'; + process.env.APPVEYOR_BUILD_FOLDER = '/'; + delete process.env.APPVEYOR_PULL_REQUEST_NUMBER; + + t.deepEqual(appveyor.configuration(), { + service: 'appveyor', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.APPVEYOR = 'true'; + process.env.APPVEYOR_JOB_NUMBER = '1234'; + process.env.APPVEYOR_REPO_COMMIT = '5678'; + process.env.APPVEYOR_BUILD_NUMBER = '91011'; + process.env.APPVEYOR_REPO_NAME = 'owner/repo'; + process.env.APPVEYOR_BUILD_FOLDER = '/'; + process.env.APPVEYOR_PULL_REQUEST_NUMBER = '10'; + process.env.APPVEYOR_REPO_BRANCH = 'master'; + + t.deepEqual(appveyor.configuration(), { + service: 'appveyor', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/buildkite.test.js b/test/buildkite.test.js new file mode 100644 index 0000000..634e19c --- /dev/null +++ b/test/buildkite.test.js @@ -0,0 +1,46 @@ +import test from 'ava'; +import buildkite from '../lib/buildkite'; + +test('Push', t => { + process.env.BUILDKITE = 'true'; + process.env.BUILDKITE_COMMIT = '5678'; + process.env.BUILDKITE_BUILD_NUMBER = '91011'; + process.env.BUILDKITE_BRANCH = 'master'; + process.env.BUILDKITE_PULL_REQUEST = 'false'; + process.env.BUILDKITE_BUILD_CHECKOUT_PATH = '/'; + process.env.BUILDKITE_ORGANIZATION_SLUG = 'owner'; + process.env.BUILDKITE_PROJECT_SLUG = 'repo'; + + t.deepEqual(buildkite.configuration(), { + service: 'buildkite', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.BUILDKITE = 'true'; + process.env.BUILDKITE_COMMIT = '5678'; + process.env.BUILDKITE_BUILD_NUMBER = '91011'; + process.env.BUILDKITE_BRANCH = 'master'; + process.env.BUILDKITE_PULL_REQUEST = '10'; + process.env.BUILDKITE_BUILD_CHECKOUT_PATH = '/'; + process.env.BUILDKITE_ORGANIZATION_SLUG = 'owner'; + process.env.BUILDKITE_PROJECT_SLUG = 'repo'; + + t.deepEqual(buildkite.configuration(), { + service: 'buildkite', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/circle.test.js b/test/circle.test.js new file mode 100644 index 0000000..7400ed0 --- /dev/null +++ b/test/circle.test.js @@ -0,0 +1,46 @@ +import test from 'ava'; +import circle from '../lib/circle'; + +test('Push', t => { + process.env.CIRCLECI = 'true'; + process.env.CIRCLE_BUILD_NUM = '1234'; + process.env.CIRCLE_SHA1 = '5678'; + process.env.CIRCLE_BRANCH = 'master'; + process.env.CIRCLE_NODE_INDEX = '1'; + process.env.CIRCLE_PROJECT_USERNAME = 'owner'; + process.env.CIRCLE_PROJECT_REPONAME = 'repo'; + delete process.env.CI_PULL_REQUEST; + + t.deepEqual(circle.configuration(), { + service: 'circleci', + commit: '5678', + build: '1234.1', + job: '1234.1', + branch: 'master', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.CIRCLECI = 'true'; + process.env.CIRCLE_BUILD_NUM = '1234'; + process.env.CIRCLE_SHA1 = '5678'; + process.env.CIRCLE_BRANCH = 'pr_branch'; + process.env.CIRCLE_NODE_INDEX = '1'; + process.env.CIRCLE_PROJECT_USERNAME = 'owner'; + process.env.CIRCLE_PROJECT_REPONAME = 'repo'; + process.env.CI_PULL_REQUEST = 'uri/pr/10'; + + t.deepEqual(circle.configuration(), { + service: 'circleci', + commit: '5678', + build: '1234.1', + job: '1234.1', + branch: 'pr_branch', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/codeship.test.js b/test/codeship.test.js new file mode 100644 index 0000000..211d715 --- /dev/null +++ b/test/codeship.test.js @@ -0,0 +1,18 @@ +import test from 'ava'; +import codeship from '../lib/codeship'; + +test('Push', t => { + process.env.CI_NAME = 'codeship'; + process.env.CI_BUILD_NUMBER = '91011'; + process.env.CI_COMMIT_ID = '5678'; + process.env.CI_BRANCH = 'master'; + process.env.CI_REPO_NAME = 'owner/repo'; + + t.deepEqual(codeship.configuration(), { + service: 'codeship', + commit: '5678', + build: '91011', + branch: 'master', + slug: 'owner/repo', + }); +}); diff --git a/test/drone.test.js b/test/drone.test.js new file mode 100644 index 0000000..750ce0d --- /dev/null +++ b/test/drone.test.js @@ -0,0 +1,48 @@ +import test from 'ava'; +import drone from '../lib/drone'; + +test('Push', t => { + process.env.DRONE = 'true'; + process.env.DRONE_JOB_NUMBER = '1234'; + process.env.DRONE_COMMIT_SHA = '5678'; + process.env.DRONE_BUILD_NUMBER = '91011'; + process.env.DRONE_BRANCH = 'master'; + process.env.DRONE_REPO_OWNER = 'owner'; + process.env.DRONE_REPO_NAME = 'repo'; + delete process.env.DRONE_PULL_REQUEST; + delete process.env.DRONE_BUILD_EVENT; + + t.deepEqual(drone.configuration(), { + service: 'drone', + commit: '5678', + build: '91011', + branch: 'master', + job: '1234', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.DRONE = 'true'; + process.env.DRONE_JOB_NUMBER = '1234'; + process.env.DRONE_COMMIT_SHA = '5678'; + process.env.DRONE_BUILD_NUMBER = '91011'; + process.env.DRONE_BRANCH = 'master'; + process.env.DRONE_PULL_REQUEST = '10'; + process.env.DRONE_BUILD_EVENT = 'pull_request'; + process.env.DRONE_REPO_OWNER = 'owner'; + process.env.DRONE_REPO_NAME = 'repo'; + + t.deepEqual(drone.configuration(), { + service: 'drone', + commit: '5678', + build: '91011', + branch: 'master', + job: '1234', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/git.test.js b/test/git.test.js new file mode 100644 index 0000000..f4edeff --- /dev/null +++ b/test/git.test.js @@ -0,0 +1,21 @@ +import test from 'ava'; +import git from '../lib/git'; +import {gitRepo, gitCommit} from './helpers/git-utils'; + +// Save the current working diretory +const cwd = process.cwd(); + +test.beforeEach(async () => { + await gitRepo('master'); +}); + +test.afterEach.always(() => { + // Restore the current working directory + process.chdir(cwd); +}); + +test.serial('Git repository', async t => { + const commit = await gitCommit(); + + t.deepEqual(git.configuration(), {commit, branch: 'master'}); +}); diff --git a/test/gitlab.test.js b/test/gitlab.test.js new file mode 100644 index 0000000..25de590 --- /dev/null +++ b/test/gitlab.test.js @@ -0,0 +1,42 @@ +import test from 'ava'; +import gitlab from '../lib/gitlab'; + +test('Push', t => { + process.env.GITLAB_CI = 'true'; + process.env.CI_COMMIT_SHA = '5678'; + process.env.CI_JOB_NAME = '91011'; + process.env.CI_JOB_STAGE = '1234'; + process.env.CI_COMMIT_REF_NAME = 'master'; + process.env.CI_REPOSITORY_URL = 'https://gitlab.com/owner/repo.git'; + process.env.CI_PROJECT_DIR = '/'; + + t.deepEqual(gitlab.configuration(), { + service: 'gitlab', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + slug: 'owner/repo', + }); +}); + +test('Push (no repo url)', t => { + process.env.GITLAB_CI = 'true'; + process.env.CI_COMMIT_SHA = '5678'; + process.env.CI_JOB_NAME = '91011'; + process.env.CI_JOB_STAGE = '1234'; + process.env.CI_COMMIT_REF_NAME = 'master'; + process.env.CI_PROJECT_DIR = '/'; + delete process.env.CI_REPOSITORY_URL; + + t.deepEqual(gitlab.configuration(), { + service: 'gitlab', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + slug: undefined, + }); +}); diff --git a/test/helpers/git-utils.js b/test/helpers/git-utils.js new file mode 100644 index 0000000..8c8c626 --- /dev/null +++ b/test/helpers/git-utils.js @@ -0,0 +1,30 @@ +import tempy from 'tempy'; +import execa from 'execa'; + +/** + * Create a git repository and change the current working directory to the repository root. + * + * @method gitRepo + * @param {String} [branch='master'] The branch to initialize. + * @return {String} The path of the repository otherwise. + */ +export async function gitRepo(branch = 'master') { + const dir = tempy.directory(); + + process.chdir(dir); + await execa('git', ['init']); + await execa('git', ['checkout', '-b', branch]); + return dir; +} + +/** + * Create commit on the current git repository. + * + * @param {String} message commit message. + * + * @returns {String} The created commit sha. + */ +export async function gitCommit(message = 'Test commit message') { + await execa('git', ['commit', '-m', message, '--allow-empty', '--no-gpg-sign']); + return execa.stdout('git', ['rev-parse', 'HEAD']); +} diff --git a/test/index.test.js b/test/index.test.js new file mode 100644 index 0000000..5e0485d --- /dev/null +++ b/test/index.test.js @@ -0,0 +1,146 @@ +import test from 'ava'; +import tempy from 'tempy'; +import m from '..'; +import {gitRepo, gitCommit} from './helpers/git-utils'; + +// Save the current working diretory +const cwd = process.cwd(); + +test.beforeEach(() => { + delete process.env.CI; + delete process.env.APPVEYOR; + delete process.env.BUILDKITE; + delete process.env.CIRCLECI; + delete process.env.CI_NAME; + delete process.env.DRONE; + delete process.env.GITLAB_CI; + delete process.env.JENKINS_URL; + delete process.env.SEMAPHORE; + delete process.env.SHIPPABLE; + delete process.env.TRAVIS; + delete process.env.WERCKER_MAIN_PIPELINE_STARTED; +}); + +test.afterEach.always(() => { + // Restore the current working directory + process.chdir(cwd); +}); + +test.serial('Appveyor', t => { + process.env.APPVEYOR = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'appveyor'); +}); + +test.serial('Buildkite', t => { + process.env.BUILDKITE = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'buildkite'); +}); + +test.serial('Circle CI', t => { + process.env.CIRCLECI = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'circleci'); +}); + +test.serial('Codeship', t => { + process.env.CI_NAME = 'codeship'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'codeship'); +}); + +test.serial('Drone', t => { + process.env.DRONE = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'drone'); +}); + +test.serial('Gitlab', t => { + process.env.GITLAB_CI = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'gitlab'); +}); + +test.serial('Jenkins', async t => { + process.env.JENKINS_URL = 'http://jenkins.jenkins.example/'; + await gitRepo(); + await gitCommit(); + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'jenkins'); +}); + +test.serial('Semaphore', async t => { + process.env.SEMAPHORE = 'true'; + await gitRepo(); + await gitCommit(); + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'semaphore'); +}); + +test.serial('Shippable', t => { + process.env.SHIPPABLE = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'shippable'); +}); + +test.serial('Travis', t => { + process.env.TRAVIS = 'true'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'travis'); +}); + +test.serial('Wercker', t => { + process.env.WERCKER_MAIN_PIPELINE_STARTED = '123456'; + + const env = m(); + t.is(env.isCi, true); + t.is(env.service, 'wercker'); +}); + +test.serial('Unknown CI and Git repository', async t => { + process.env.CI = 'true'; + await gitRepo(); + await gitCommit(); + + const env = m(); + t.is(env.isCi, true); + t.falsy(env.service); +}); + +test.serial('Unknown CI and not a Git repository', t => { + process.env.CI = 'true'; + process.chdir(tempy.directory()); + const env = m(); + + t.is(env.isCi, true); + t.falsy(env.service); +}); + +test.serial('Not CI', t => { + process.chdir(tempy.directory()); + const env = m(); + + t.is(env.isCi, false); + t.falsy(env.service); +}); diff --git a/test/jenkins.test.js b/test/jenkins.test.js new file mode 100644 index 0000000..50e1473 --- /dev/null +++ b/test/jenkins.test.js @@ -0,0 +1,71 @@ +import test from 'ava'; +import jenkins from '../lib/jenkins'; + +test('Push', t => { + process.env.JENKINS_URL = 'http://jenkins.jenkins.example/'; + process.env.GIT_COMMIT = '5678'; + process.env.GIT_BRANCH = 'master'; + process.env.BUILD_NUMBER = '91011'; + process.env.WORKSPACE = '/'; + delete process.env.BRANCH_NAME; + delete process.env.ghprbActualCommit; + delete process.env.ghprbSourceBranch; + delete process.env.CHANGE_ID; + delete process.env.ghprbPullId; + + t.deepEqual(jenkins.configuration(), { + service: 'jenkins', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + pr: undefined, + isPr: false, + }); +}); + +test('PR', t => { + process.env.JENKINS_URL = 'http://jenkins.jenkins.example/'; + process.env.GIT_COMMIT = '5678'; + process.env.BRANCH_NAME = 'pr_branch'; + process.env.BUILD_NUMBER = '91011'; + process.env.WORKSPACE = '/'; + process.env.CHANGE_ID = '10'; + delete process.env.GIT_BRANCH; + delete process.env.ghprbActualCommit; + delete process.env.ghprbSourceBranch; + delete process.env.ghprbPullId; + + t.deepEqual(jenkins.configuration(), { + service: 'jenkins', + commit: '5678', + build: '91011', + branch: 'pr_branch', + root: '/', + pr: '10', + isPr: true, + }); +}); + +test('PR (PR builder)', t => { + process.env.JENKINS_URL = 'http://jenkins.jenkins.example/'; + process.env.ghprbActualCommit = '5678'; + process.env.ghprbSourceBranch = 'pr_branch'; + process.env.BUILD_NUMBER = '91011'; + process.env.WORKSPACE = '/'; + process.env.ghprbPullId = '10'; + delete process.env.GIT_BRANCH; + delete process.env.GIT_COMMIT; + delete process.env.BRANCH_NAME; + delete process.env.CHANGE_ID; + + t.deepEqual(jenkins.configuration(), { + service: 'jenkins', + commit: '5678', + build: '91011', + branch: 'pr_branch', + root: '/', + pr: '10', + isPr: true, + }); +}); diff --git a/test/semaphore.test.js b/test/semaphore.test.js new file mode 100644 index 0000000..c016a05 --- /dev/null +++ b/test/semaphore.test.js @@ -0,0 +1,57 @@ +import test from 'ava'; +import semaphore from '../lib/semaphore'; +import {gitRepo, gitCommit} from './helpers/git-utils'; + +// Save the current working diretory +const cwd = process.cwd(); + +test.beforeEach(async () => { + await gitRepo(); +}); + +test.afterEach.always(() => { + // Restore the current working directory + process.chdir(cwd); +}); + +test.serial('Push', async t => { + const commit = await gitCommit(); + process.env.SEMAPHORE = 'true'; + process.env.SEMAPHORE_BUILD_NUMBER = '91011'; + process.env.BRANCH_NAME = 'master'; + process.env.SEMAPHORE_PROJECT_DIR = '/'; + process.env.SEMAPHORE_REPO_SLUG = 'owner/repo'; + delete process.env.PULL_REQUEST_NUMBER; + + t.deepEqual(semaphore.configuration(), { + service: 'semaphore', + commit, + build: '91011', + branch: 'master', + root: '/', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test.serial('PR', async t => { + const commit = await gitCommit(); + process.env.SEMAPHORE = 'true'; + process.env.SEMAPHORE_BUILD_NUMBER = '91011'; + process.env.BRANCH_NAME = 'master'; + process.env.PULL_REQUEST_NUMBER = '10'; + process.env.SEMAPHORE_PROJECT_DIR = '/'; + process.env.SEMAPHORE_REPO_SLUG = 'owner/repo'; + + t.deepEqual(semaphore.configuration(), { + service: 'semaphore', + commit, + build: '91011', + branch: 'master', + root: '/', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/shippable.test.js b/test/shippable.test.js new file mode 100644 index 0000000..9773ec8 --- /dev/null +++ b/test/shippable.test.js @@ -0,0 +1,52 @@ +import test from 'ava'; +import shippable from '../lib/shippable'; + +test('Push', t => { + process.env.SHIPPABLE = 'true'; + process.env.JOB_NUMBER = '1234'; + process.env.COMMIT = '5678'; + process.env.BUILD_NUMBER = '91011'; + process.env.BRANCH = 'master'; + process.env.PULL_REQUEST = 'false'; + process.env.IS_PULL_REQUEST = 'false'; + process.env.SHIPPABLE_BUILD_DIR = '/'; + process.env.SHIPPABLE_REPO_SLUG = 'owner/repo'; + delete process.env.BASE_BRANCH; + + t.deepEqual(shippable.configuration(), { + service: 'shippable', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.SHIPPABLE = 'true'; + process.env.JOB_NUMBER = '1234'; + process.env.COMMIT = '5678'; + process.env.BUILD_NUMBER = '91011'; + process.env.BASE_BRANCH = 'master'; + process.env.PULL_REQUEST = '10'; + process.env.IS_PULL_REQUEST = 'true'; + process.env.SHIPPABLE_BUILD_DIR = '/'; + process.env.SHIPPABLE_REPO_SLUG = 'owner/repo'; + delete process.env.BRANCH; + + t.deepEqual(shippable.configuration(), { + service: 'shippable', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/travis.test.js b/test/travis.test.js new file mode 100644 index 0000000..e16def4 --- /dev/null +++ b/test/travis.test.js @@ -0,0 +1,48 @@ +import test from 'ava'; +import travis from '../lib/travis'; + +test('Push', t => { + process.env.TRAVIS = 'true'; + process.env.TRAVIS_JOB_NUMBER = '1234'; + process.env.TRAVIS_COMMIT = '5678'; + process.env.TRAVIS_BUILD_NUMBER = '91011'; + process.env.TRAVIS_BRANCH = 'master'; + process.env.TRAVIS_PULL_REQUEST = 'false'; + process.env.TRAVIS_BUILD_DIR = '/'; + process.env.TRAVIS_REPO_SLUG = 'owner/repo'; + + t.deepEqual(travis.configuration(), { + service: 'travis', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: undefined, + isPr: false, + slug: 'owner/repo', + }); +}); + +test('PR', t => { + process.env.TRAVIS = 'true'; + process.env.TRAVIS_JOB_NUMBER = '1234'; + process.env.TRAVIS_COMMIT = '5678'; + process.env.TRAVIS_BUILD_NUMBER = '91011'; + process.env.TRAVIS_BRANCH = 'master'; + process.env.TRAVIS_PULL_REQUEST = '10'; + process.env.TRAVIS_BUILD_DIR = '/'; + process.env.TRAVIS_REPO_SLUG = 'owner/repo'; + + t.deepEqual(travis.configuration(), { + service: 'travis', + commit: '5678', + build: '91011', + branch: 'master', + root: '/', + job: '1234', + pr: '10', + isPr: true, + slug: 'owner/repo', + }); +}); diff --git a/test/wercker.test.js b/test/wercker.test.js new file mode 100644 index 0000000..ef6b50c --- /dev/null +++ b/test/wercker.test.js @@ -0,0 +1,20 @@ +import test from 'ava'; +import wercker from '../lib/wercker'; + +test('Push', t => { + process.env.WERCKER_MAIN_PIPELINE_STARTED = '123456'; + process.env.WERCKER_GIT_COMMIT = '5678'; + process.env.WERCKER_GIT_BRANCH = 'master'; + process.env.WERCKER_ROOT = '/'; + process.env.WERCKER_GIT_OWNER = 'owner'; + process.env.WERCKER_GIT_REPOSITORY = 'repo'; + + t.deepEqual(wercker.configuration(), { + service: 'wercker', + commit: '5678', + build: '123456', + branch: 'master', + root: '/', + slug: 'owner/repo', + }); +});